/*
 *     File: TestSet.h
 *
 *  Created: 11-mar-2007
 *  Updated: not yet
 *  Purpose: A framework for unit testing
 *  Version: 1.0      TWI 200703011
 *           - Initial version
 *  Version: 1.0.1    AKK 20070322
 *             - NOT_REACHED clause added
 *             + Uses TestSet_output_not_reached_failed(...)
 *  Version: 1.0.2    TWI 20070323
 *           - Changed all tabs to spaces
 *           - Made versioning comments consistent
 *  Version: 1.1     TWI 20070323
 *           - Added namespaces with Setup and Teardown
 *  Version: 1.2      TWI 20080603
 *           - added support for testcase and testset setup (setup once)
 *  TODO: make it C compatible again while retaining C++ compatibility
 */

#ifndef __TEST_SET
#define __TEST_SET

/*
 *  System Includes
 */
#include <signal.h>  // for SIGABRT

#include "TestSet_output.hpp"

/*
 *  Module defines
 */
typedef enum
{
   TEST_CASE_MAGIC = 0xA7A7A7A7,
   TEST_SET_MAGIC =  0x7A7A7A7A,
   TEST_SET_DESTRUCTED = 0x96969696
} TestSet_magic_enum;

typedef bool (*TestSetupFunction)(void);
typedef bool (*TestTeardownFunction)(void);



static const bool OK = true;

#define TEST_SET(name)                                             \
            test_set test_set__ ## name = {                        \
                   TEST_SET_MAGIC,                                 \
                   #name,                                          \
                   0,                                              \
                   0,                                              \
                   0,                                              \
                   0,                                              \
                   false,                                          \
                   false,                                          \
                   0,                                              \
                   0,                                              \
                   0,                                              \
                   0,                                              \
                   0,                                              \
                   0,                                              \
                   0,                                              \
                   TestSet_add_set( & test_set__ ## name )         \
            };

#define TEST(test)                                                 \
            bool test();                                           \
            TEST_SET_ADD(test)                                     \
            bool test()                                            \
            {                                                      \
                bool result = OK;

#define END_TEST                                                   \
                return result;                                     \
            }


#define SETUP(name)                                                \
            bool setup_ ## name();                                 \
            TEST_SET_SETUP(setup_ ## name);                        \
            bool setup_ ## name()                                  \
            {                                                      \
                bool result = OK;

#define END_SETUP                                                  \
	        END_TEST


#define TEARDOWN(name)                                             \
            bool teardown_ ## name();                              \
            TEST_SET_TEARDOWN(teardown_ ## name);                  \
            bool teardown_ ## name()                               \
            {                                                      \
                bool result = OK;

#define END_TEARDOWN                                               \
            END_TEST


#define SETUP_ONCE(name)                                           \
            bool setup_once_ ## name();                            \
            TEST_SET_SETUP_ONCE(setup_once_ ## name);              \
            bool setup_once_ ## name()                             \
            {                                                      \
                bool result = OK;

#define END_SETUP_ONCE                                             \
	        END_TEST


#define TEARDOWN_ONCE(name)                                        \
            bool teardown_once_ ## name();                         \
            TEST_SET_TEARDOWN_ONCE(teardown_once_ ## name);        \
            bool teardown_once_ ## name()                          \
            {                                                      \
                bool result = OK;

#define END_TEARDOWN_ONCE                                          \
            }


#define TEST_SET_ADD(test)                                         \
       static TestCase test_case ## test = {                       \
          TEST_CASE_MAGIC,                                         \
          #test,                                                   \
          (test),                                                  \
          TestSet_add_test_case( & test_case ## test )             \
       };

#define TEST_SET_SETUP(name)                                       \
       static TestSetupFunction _ ## name =                        \
          TestSet_set_setup( &name );

#define TEST_SET_TEARDOWN(name)                                    \
       static TestTeardownFunction _ ## name =                     \
          TestSet_set_teardown( &name );

#define TEST_SET_SETUP_ONCE(name)                                  \
       static TestSetupFunction _ ## name =                        \
          TestSet_set_setup_once( &name );

#define TEST_SET_TEARDOWN_ONCE(name)                               \
       static TestTeardownFunction _ ## name =                     \
          TestSet_set_teardown_once( &name );

#define VERIFY(expression)                                         \
    if ( result == OK && !(expression) )                           \
    {                                                              \
       result = !OK;                                               \
       TestSet_output_verification_failed(__FILE__, __LINE__,      \
                                          #expression);            \
    }

#define CALL(test)            VERIFY(test())
#define CALL_SETUP(name)      VERIFY(setup_ ## name());
#define CALL_TEARDOWN(name)   teardown_ ## name();

#define EXPECT_NOT_REACHED                                         \
    if ( result == OK )                                            \
    {                                                              \
       result = !OK;                                               \
       TestSet_output_not_reached_failed(__FILE__, __LINE__);      \
    }

#define EXPECT_SYSTEM_ASSERTION_FAILED                             \
    TestSet_output_expect_assertion_failed();                      \
    TestSet_expect_signal(SIGABRT);

#define EXPECT_EXCEPTION( exception_type, source_code )            \
	{                                                              \
		bool exception_caught = false;                             \
                                                                   \
		try                                                        \
		{                                                          \
		    source_code;                                           \
		}                                                          \
		catch ( exception_type assert_exception )                  \
		{                                                          \
			exception_caught = true;                               \
		}                                                          \
		CHECK( exception_caught );                                 \
	}

#define EXPECT_SIGNAL(signal)                                      \
    TestSet_output_expect_signal(signal);                          \
    TestSet_expect_signal(signal);

#define CHECK VERIFY


#define TEST_SET_SAFE_DELETE( type, variable )                    \
	{ type* tmp = variable; variable = 0 ; delete tmp; }


#ifdef  WIN32    // voor DevCpp
    #define WAIT system("PAUSE");
#else
    #define WAIT
#endif  /* WIN32 */

#define TEST_SET_EXECUTE                                           \
    int main()                                                     \
    {                                                              \
         int  result = TestSet_execute();                          \
                                                                   \
         WAIT                                                      \
                                                                   \
         return result;                                            \
    }


/*
 *  Module typedefinitions
 */
typedef bool (*TestSet_test)(void);

typedef struct test_case
{
   TestSet_magic_enum magic;
          const char* name;
         TestSet_test test;
           test_case* next;
} TestCase;

typedef struct test_set
{
     TestSet_magic_enum magic;
            const char* name;
                    int passed;
                    int failed;
                    int total;
                    int nr_of_testcases;
                   bool setup_once_called;
                   bool setup_once_result;
     TestSetupFunction  setup_once;
   TestTeardownFunction teardown_once;
      TestSetupFunction setup;
   TestTeardownFunction teardown;
              TestCase* head;
              TestCase* tail;
              TestCase* current;
              test_set* next;
} TestSet;

/*
 *  Module Exported Global data
 */

/*
 *  Module Exported functions
 */
extern             TestSet*           TestSet_add_set( TestSet* test_set );
extern            TestCase*     TestSet_add_test_case( TestCase* test_case );
extern    TestSetupFunction         TestSet_set_setup( TestSetupFunction setup );
extern TestTeardownFunction      TestSet_set_teardown( TestSetupFunction teardown );
extern    TestSetupFunction    TestSet_set_setup_once( TestSetupFunction setup_once );
extern TestTeardownFunction TestSet_set_teardown_once( TestSetupFunction teardown_once );

extern void TestSet_expect_signal( int a_signal );
extern  int TestSet_execute();

#endif  // __TEST_SET
